package com.dimples.security.captcha;

import com.dimples.common.constant.DimplesConst;
import com.dimples.common.constant.ImageTypeConst;
import com.dimples.common.helper.RedisCache;
import com.dimples.common.properties.CaptchaProperties;
import com.dimples.common.properties.DimplesProperties;
import com.dimples.security.exception.ValidateCodeException;
import com.wf.captcha.GifCaptcha;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;

import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;

/**
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2021/6/24
 */
@Service("captchaHelper")
@RequiredArgsConstructor
public class ImageCodeHelper {

    private final RedisCache redisCache;
    private final DimplesProperties properties;

    /**
     * 创建验证码
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     * @throws Exception Exception
     */
    public void createCaptcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
        CaptchaProperties captchaProperties = properties.getCaptcha();

        setHeader(response, captchaProperties.getType());
        Captcha captcha = createCaptcha(captchaProperties);
        CaptchaCode captchaCode = new CaptchaCode(StringUtils.lowerCase(captcha.text()), captchaProperties.getExpire());

        Map<String, String> paramMap = ServletUtil.getParamMap(request);

        String codeKey = DimplesConst.KEY_CAPTCHA_PREFIX + paramMap.get(DimplesConst.KEY);
        // 验证码存入缓存
        redisCache.setCacheObject(codeKey, JSONUtil.parseObj(captchaCode), captchaProperties.getExpire(), TimeUnit.SECONDS);

        // 输出图片流
        captcha.out(response.getOutputStream());
    }

    private Captcha createCaptcha(CaptchaProperties captchaProperties) {
        Captcha captcha;
        // 三个参数分别为宽、高、位数
        if (StringUtils.equalsIgnoreCase(captchaProperties.getType(), ImageTypeConst.GIF)) {
            captcha = new GifCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight(), captchaProperties.getLen());
        } else {
            captcha = new SpecCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight(), captchaProperties.getLen());
        }
        // 设置类型，纯数字、纯字母、字母数字混合
        captcha.setCharType(captchaProperties.getCharType());
        return captcha;
    }

    private void setHeader(HttpServletResponse response, String type) {
        // 设置请求头为输出图片类型
        if (StringUtils.equalsIgnoreCase(type, ImageTypeConst.GIF)) {
            response.setContentType(MediaType.IMAGE_GIF_VALUE);
        } else {
            response.setContentType(MediaType.IMAGE_PNG_VALUE);
        }
        response.setHeader(HttpHeaders.PRAGMA, "No-cache");
        response.setHeader(HttpHeaders.CACHE_CONTROL, "No-cache");
        response.setDateHeader(HttpHeaders.EXPIRES, 0L);
    }

    /**
     * 验证验证码
     *
     * @param request HttpServletRequest
     */
    public void validateCode(HttpServletRequest request) {
        String codeInRequest = ServletUtil.getParamMap(request).get(DimplesConst.KEY_WEB_CAPTCHA);
        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException("验证码不能为空！");
        }

        String codeKey = DimplesConst.KEY_CAPTCHA_PREFIX + ServletUtil.getParamMap(request).get(DimplesConst.KEY);
        CaptchaCode captchaCode;

        Object codeInRedis = redisCache.getCacheObject(codeKey);

        if (codeInRedis != null) {
            captchaCode = JSONUtil.toBean(JSONUtil.parseObj(codeInRedis), CaptchaCode.class);
            redisCache.deleteObject(codeKey);
        } else {
            throw new ValidateCodeException("验证码不存在，请重新发送！");
        }

        if (captchaCode.isExpire()) {
            throw new ValidateCodeException(StrUtil.format("验证码【{}】已过期，请重新发送！", codeInRequest));
        }
        if (!StrUtil.equalsIgnoreCase(captchaCode.getCode(), codeInRequest)) {
            throw new ValidateCodeException("验证码不正确！");
        }

    }

}
